<?php namespace xlx;
use Workerman\Connection\TcpConnection;
use Workerman\Connection\AsyncTcpConnection;
use \Workerman\Events\EventInterface;

class Server 
{
    public static function init($opt)
    {
        $event = Process::createEventLoop(); 
        $class = get_called_class();
        $server = \stream_socket_server($opt['addr'], $errno, $errstr);
        $event->add($server, EventInterface::EV_READ, function ($socket)use($class,$opt) {
            $socket = \stream_socket_accept($socket, 0, $opt['addr']);
            $client = new TcpConnection($socket,$opt['addr']);
            if(isset($opt['protocol'])){
                $client->protocol=$opt['protocol'];
            }
            $client->onMessage=[$class,'onMessage'];
            $client->onClose=[$class,'onClose'];
            $class::onConnect($client);
        });
        $class::onStart($server,$opt);
        $event->loop();
    }
    public static function onStart($server,$opt) { }
    public static function onConnect($conn)  {  }
    public static function onMessage($conn, $data)  {  }
    public static function onClose($conn)  {  }
}

class RegisterServer extends Server
{
    public static $addr = 'tcp://127.0.0.1:128458';
    public static $protocol =  '\xlx\Protocols\Frame'; 
    public static function start() {
        Process::exec(function(){ 
            @stream_socket_client(RegisterServer::$addr) and exit("register server is running\n");
            RegisterServer::init(['addr'=>RegisterServer::$addr,'protocol'=>RegisterServer::$protocol]);//block loop 
        });
    }
    public static $conns=[];
    public static function onConnect($conn){
        $conn->addr = \stream_socket_get_name($conn->getSocket(),true);
        self::$conns[$conn->addr]=$conn; 
    }
    public static function onClose($conn){
        unset(self::$conns[$conn->addr]);  
    }
    public static function onMessage($conn, $data)
    {
        switch ($data->act) {
            case 'whereis': 
                $conn->send('my');
                break;  
            case 'channel':     
                self::$conns[$data->pid]->close($data->result);
                break;
            default: 
                break;
        }
    }  
 
    public static $channel=[];
    public static function newChannel(){  
        $socket = @\stream_socket_client(self::$addr,$errno,$errstr) or exit("register server is not exists\n");    
        $pid = \stream_socket_get_name($socket, false);    
        self::$channel[$pid]=$socket; 
        return $pid;
    }
    public static function waitChannel($pid){ 
        $result = stream_get_contents(self::$channel[$pid]); 
        fclose(self::$channel[$pid]);
        unset(self::$channel[$pid]);
        return $result;
    }
    public static function sendChannel($pid,$data){ 
        return self::postData(['act'=>'channel','pid'=>$pid,'result'=>$data]);  
    }
    public static function postData($data){  
        $socket = @\stream_socket_client(self::$addr,$errno,$errstr) or exit("register server is not exists\n"); ;  
        fwrite($socket,self::$protocol::encode($data));
        return $socket;
    }
}


class Node 
{
    public function init($opt){  
        $class = get_called_class();
        //client 
        $server = \stream_socket_client(\xlx\RegisterServer::$addr, $errno, $errstr);
        $cconn = new TcpConnection($socket,$opt['addr']);
        

        //server
        $server = \stream_socket_server($opt['addr'], $errno, $errstr);
        $event->add($server, EventInterface::EV_READ, function ($socket)use($class,$opt) {
            $socket = \stream_socket_accept($socket, 0, $opt['addr']);
            $sconn = new TcpConnection($socket,$opt['addr']);
            if(isset($opt['protocol'])){
                $sconn->protocol=$opt['protocol'];
            }
            $sconn->onMessage=[$class,'onMessage'];
            $sconn->onClose=[$class,'onClose'];
            $class::onConnect($sconn);
        });
        $class::onStart($server,$opt); 
    }

}



// class Worker extends Server
// {
//     public static function start($opt) : string { 
//         $pid = \xlx\RegisterServer::newChannel();
//         Process::exec(function()use($pid,$opt){  
//             $opt['socket'] = @\stream_socket_client(self::$addr,$errno,$errstr) or exit("register server is not exists\n"); 
//             $opt['addr'] = \stream_socket_get_name($opt['socket'], false);
//             $opt['pid'] = $pid;
//             Worker::init($opt); 
//         }); 
//         return \xlx\RegisterServer::waitChannel($pid);
//     }
//     public static function onStart($server,$opt){
//         $conn = new TcpConnection($opt['socket'],$opt['addr']); 
//         $conn->send(['act'=>'channel','pid'=>$opt['pid'],'result'=>$opt['addr']]);  

//     }
// }




// class Gateway 
// { 
// } 





/*
class Gateway 
{
    static $opt=[
        'name'=>__CLASS__,
        'listen'=>'tcp://0.0.0.0:8080',
        'protocol'=>'\xlx\Protocols\Http',
        //'forkprocess'=>'4',//linux only
        // 'maxprocess'=>'64',
        // 'minprocess'=>'2',
        'cycle'=>'30',                          //
    ];
    public static function start($class,$opt){  
        $opt = array_merge(self::$opt,$opt);
        $server_addr = \xlx\Process::spawn(function()use($class,$opt){
            $class::gateway($opt);
        }); 
    }  
    public static $workers = [];
    public static function gateway($addr,$opt){
        $event = Process::createEventLoop();  
        $class = get_called_class();
        $server = \stream_socket_server($opt['listen'], $errno, $errstr);
        $event->add($server, EventInterface::EV_READ, function ($socket) { 
            $socket = \stream_socket_accept($socket, 0, $addr); 
            $client = new TcpConnection($socket);
            $worker = new AsyncTcpConnection('tcp://'.self::$workers[\array_rand(self::$workers)]); 
            $client->pipe($worker);
            $worker->pipe($client);
            $worker->connect(); 
        });     
        for ($i=0; $i < 4; $i++) { 
            self::$workers[] = Process::spawn(function()use($class,$i){
                $class::worker(['i'=>$i]);
            });   
        }  
        $event->loop();
    }
    public static function worker($addr,$opt){  
        $event = Process::createEventLoop(); 
        $server = \stream_socket_server($addr,$errno,$errstr);
        $event->add($server, EventInterface::EV_READ, function ($socket)use($event,$opt) { 
            $client = \stream_socket_accept($socket, 0, $addr);
            $conn = new TcpConnection($client); 
            $object = new static; 
            $conn->onMessage = [$object,'onMessage'];
            $conn->onClose =[$object,'onClose'];
            $object->onConnect($conn,$opt);
        }); 
        $event->loop();
    }

    public function onConnect($conn,$opt){ 
        echo 'ping:'.$opt['id'];
        $conn->close();
    }
    public function onMessage($conn,$data){ 


    }
    public function onClose($conn){ 
    }
}
/*  

gateway.event>
    listen.accept>
        .event@add recv
    ^
    client.recv>

    ^
    client.recv+
    ...
^

 



# worker[w1] > spawn*4 ^
Process::call('w1'):mixed;//block
Process::cast('w1'):promise;//noblock  
yield Process::cast('w1'):mixed;//yield 

Process::spawn(function(){ echo '';});

Watch::start(['name'=>'sup'],[
    Worker::start(['name'=>'w1']),
    Worker::start(['name'=>'w2']),
]);

Worker::start(['name'=>'w1' ] );
Worker::start(['name'=>'w1','maxprocess'=>4],[
    //.auto
]);

# gateway[gw1]*4 > spawn*4
Gateway1::start(['name'=>'gw1'] );
class Gateway1 extends Gateway{
    function onconnect($conn){

    }
    function onmessage($conn){

    }
    function onclose($conn){

    }
}


*/
